package com.info.zhiduoduo.weixin.interceptor;

import com.alibaba.fastjson.JSON;
import com.info.zhiduoduo.common.annotation.ZddLog;
import com.info.zhiduoduo.common.constants.StatusMsg;
import com.info.zhiduoduo.common.constants.system.ZddAppEnum;
import com.info.zhiduoduo.common.exception.ApiException;
import com.info.zhiduoduo.common.exception.SmartException;
import com.info.zhiduoduo.common.utils.ContextUtil;
import com.info.zhiduoduo.common.utils.LogUtil;
import com.info.zhiduoduo.common.utils.ReflectionUtil;
import com.info.zhiduoduo.common.utils.StringUtil;
import com.info.zhiduoduo.core.configs.jwt.UserLocal;
import com.info.zhiduoduo.core.dto.operationlog.OperationLogContext;
import com.info.zhiduoduo.core.service.common.AuthService;
import com.info.zhiduoduo.core.service.common.OperationLogHelper;

import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;

/**
 * Created by TerryJ on 2016就/03/14.
 */
@Slf4j
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class RequestBodyAspect {

	@Autowired
	HttpServletRequest httpServletRequest;
	@Autowired
	AuthService authService;
	@Autowired
	OperationLogHelper operationLogHelper;

	@Value("${auth.token.check}")
	private boolean authTokenCheck;

	AntPathMatcher antPathMatcher = new AntPathMatcher();

	private static Set<String> tokenWhiteList = new HashSet<String>() {{
		// 登录
		add("/wx/notify");
		add("/wx/silentAuth");
		add("/wx/authPage");
		add("/wx/userState");
		add("/v1/getUserInfo");
		add("/v1/caseList");
		add("/wx/share");
		add("/wxpay/notify");

		// 下载
		add("/invoice/downloadInvoice");

		add("/comp/v1/alterLoginInfo");
		add("/evaluate/tag/queryEvaluateTagByUserType");
		add("/notice/detail");
		add("/partner/getShareImg");
		add("/evaluate/saveEvaluate");
		add("/project/downloadProjectDeliveryZipFile");
		add("/comp/getEnterpriseAgreement");

		add("/common/sendPhoneCode");
		add("/common/downloadPath");

		add("/partner/addShareUser");
		add("/partner/createShareImg");
		add("/partner/savePartnerVaccineRecord");
		add("/partner/getVaccinePoster");

		add("/marketingCooperative/getPartnerPosterForm");
		add("/marketingCooperative/generatePartnerPoster");
		add("/marketingCooperative/savePartnerMarketingCooperativeRecordRecord");
	}};

	@Pointcut("execution(public * com.info.zhiduoduo.weixin.controller..*(..))")
	public void excudeService() {
	}

	@Around("excudeService()")
	public Object around(ProceedingJoinPoint jp) throws Throwable {
		String url = httpServletRequest.getRequestURI();
		StringBuilder sb = new StringBuilder();
		long start = System.currentTimeMillis();

		Object[] args = jp.getArgs();
		MethodSignature ms = (MethodSignature) jp.getSignature();
		Method method = ms.getMethod();
		String methodName = ms.getName();
		Parameter[] parameters = method.getParameters();
		Object requestBody = ReflectionUtil.findRequestBody(args, parameters);

		LogUtil.info("ACTION START====================【Method : " + methodName + "】【RequestURL : " + url + "】====================");
		Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
		if (parameterMap != null && !parameterMap.isEmpty()) {
			LogUtil.info("url参数或表单参数=" + JSON.toJSONString(parameterMap));
		}
		if (requestBody != null) {
			LogUtil.info("RequestBody参数=" + JSON.toJSONString(requestBody));
		}

		try {
			sb.append("项目：【 塾人微信端 】\n")
					.append("环境：【 ")
					.append(ContextUtil.getActiveProfile())
					.append(" 】\n")
					.append("URL：")
					.append(url)
					.append("\n")
					.append("方法：")
					.append(jp.getTarget().getClass().getName())
					.append(".")
					.append(method.getName())
					.append("()\n");
			if (MapUtils.isNotEmpty(parameterMap)) {
				sb.append("url参数或表单参数 : ").append(JSON.toJSON(parameterMap).toString()).append("\n");
			}
			if (requestBody != null) {
				sb.append("BODY请求参数 : ").append(JSON.toJSONString(requestBody)).append("\n");
			}

			// 验证登录token
			checkToken();

			// 准备记录操作日志
			OperationLogContext operationLogContext = null;
			ZddLog zddLog = AnnotationUtils.getAnnotation(method, ZddLog.class);
			if (zddLog != null) {
				try {
					operationLogContext = operationLogHelper.doBeforeOperate(zddLog, ZddAppEnum.WECHAT, jp, url,
							JSON.toJSONString(parameterMap));
				} catch (Exception e) {
					log.error("记录操作日志， doBeforeOperate 报错", e);
				}
			}

			// 继续执行方法
			Object obj = jp.proceed();

			// 记录日志
			if (operationLogContext != null) {
				try {
					operationLogHelper.doPostOperate(obj, operationLogContext);
				} catch (Exception e) {
					log.error("记录操作日志， doPostOperate 报错", e);
				}
			}

			String resultStr = JSON.toJSONString(obj);
			if (StringUtil.isNotEmpty(resultStr) && resultStr.length() > 1000) {
				resultStr = resultStr.substring(0, 1000) + "  ......  太长了不打印 ！！！";
			}
			LogUtil.info("返回result=" + resultStr);

			// 清除UserLocal中的用户数据
			UserLocal.clear();

			return obj;
		} catch (SmartException e) {
			sb.append("code: ").append(e.getState()).append(", msg: ").append(e.getMessage());
			throw new ApiException(sb.toString(), e);
		} catch (Exception e) {
			throw new ApiException(StatusMsg.StatusMsg_198, sb.toString(), e);
		} finally {
			long requestTime = System.currentTimeMillis() - start;
			LogUtil.info("消耗的时间(毫秒): " + requestTime);
			LogUtil.info("ACTION END====================【Method : " + methodName + "】【RequestURL : " + url + "】====================");
		}
	}

	public void checkToken() {
		if (!authTokenCheck) return;

		String url = httpServletRequest.getRequestURI();
		// 判断是否需要验证
		if (tokenWhiteList.stream().anyMatch(path -> antPathMatcher.match(path, url))) {
			log.info("当前接口在tokenWhiteList中，跳过验证token");
			return;
		}
		authService.check(httpServletRequest);
	}
}
